// Behaviors ===================================================================
//==============================================================================
//Our reusable behaviors

var SelectLoad = Behavior.create({
	initialize: function(urlBase, selectTarget, blankFirst) {
		this.urlBase = urlBase;
		this.selectTarget = $(selectTarget);
		this.blankFirst = blankFirst || false;
	},
	onchange: function() {
		var selectTarget = this.selectTarget;
		var blankFirst = this.blankFirst;
		new Ajax.Request(this.urlBase+this.element.value, {
			method: 'get',
			onSuccess: function(transport) {
				selectTarget.update();
				if(blankFirst){
					var option = document.createElement('option');
					selectTarget.insert(option);
				}
				data = transport.responseText.evalJSON();
				data = $H(data);
				data.each(function(state) {
					var option = new Element('option', { 'value':state.key});
					option.insert(state.value);
					selectTarget.insert(option);
				});
			}
		});
	}
});

var MarkDirty = Behavior.create({
	initialize: function(className) {
		this.className = className || 'checkbox';
	},
	onclick: function(e) {
		var target = Event.element(e)
		if($(target).hasClassName(this.className)){
			$(target).addClassName('dirty');
		}
	}
});

var Altered = Behavior.create({
	initialize: function(className, updateField, updateValue) {
		this.className = className || 'alter';
		this.updateField = updateField || 'altered';
		this.updateValue = updateValue || '1';
	},
	onchange: function(e) {
		var target = Event.element(e)
		if($(target).hasClassName(this.className)){
			$(this.updateField).value = this.updateValue;
		}
	}
});

var ToolTip = Behavior.create({
	initialize: function(id, settings) {
		this.settings = settings; //should handle some default settings
		this.content = $(id);	
		this.__setDimensions();
		this.settings.position = {
			left: 0,
			top: 0
		}		
	},
	onmouseover: function(e) {
		var element = Event.element(e);
		this.__setOffset(element);	
		//shift the content and get its current position
		this.content.absolutize();
		this.content.setStyle({
			width: this.settings.dimensions.width + 'px',
			height: this.settings.dimensions.height + 'px'
		});

		//get the position of the element
		var position = this.__getPositionOnPage(element);
		var scrollOffsets = document.viewport.getScrollOffsets();

		if(!window.XMLHttpRequest) { //for ie6
			var iframe = new Element('iframe');
			iframe.id = 'iframe';
			iframe.setStyle({
				width: this.settings.dimensions.width + 'px',
				height: this.settings.dimensions.height + 'px',
				top: (position.top + scrollOffsets.top + Number(this.settings.position.top)) + 'px',
				left: (position.left + scrollOffsets.left + Number(this.settings.position.left)) + 'px',
				display: 'block',
				position: 'absolute'
			});
			var body = $$('body')[0];
			Element.insert(body, iframe);
			var div = Element.remove(this.content);
			Element.insert(body, div);
		}

		//show the content and offset it
		this.content.setStyle({		
			top: (position.top + scrollOffsets.top + Number(this.settings.position.top)) + 'px',
			left: (position.left + scrollOffsets.left + Number(this.settings.position.left)) + 'px',
			display: "block"
		});

		Effect.Appear(this.content, {duration: .3});
	},
	onmouseout: function(e) {
		//hide the content
		Effect.Fade(this.content, {duration: .3});
		if(!window.XMLHttpRequest) { //for ie6
			Element.remove('iframe');
		}
	},
	__setDimensions: function() {
		this.content.setStyle({
			width: this.settings.width + 'px'
		});
		this.settings.dimensions = {
			height: this.content.getHeight(),
			width: this.settings.width
		}
	},
	__getPositionOnPage: function(element) {
		var p = element.viewportOffset();
		
	    // find coordinate system to use
	    var delta = [0, 0];
	    var parent = null;
	    // delta [0,0] will do fine with position: fixed elements,
	    // position:absolute needs offsetParent deltas
	    if (Element.getStyle(element, 'position') == 'absolute') {
	      parent = element.getOffsetParent();
	      delta = parent.viewportOffset();
	    }

	    // correct by body offsets (fixes Safari)
	    if (parent == document.body) {
	      delta[0] -= document.body.offsetLeft;
	      delta[1] -= document.body.offsetTop;
	    }

	    // set position
		var position = {
			left: Number(p[0] - delta[0]),
			top: Number(p[1] - delta[1])
		}

		return position;
	},
	__setOffset: function(element) {
		var eHeight = element.getHeight();
		var eWidth = element.getWidth();
		
		// set horizontal offset
		switch(this.settings.hOffset) {
			case "left" :
				this.settings.position.left = 0 - Number(this.settings.dimensions.width) - Number(this.settings.offsetPadding) + Number(eWidth)
			break;
			
			case "right" :
				this.settings.position.left = 0
			break;
			
			default :
				this.settings.position.left = 0
			break;
		}
		
		//set vertical offset
		switch(this.settings.vOffset) {
			case "top" :
				this.settings.position.top = 0 - Number(this.settings.dimensions.height) - Number(this.settings.offsetPadding)
			break;
			
			case "bottom" :
				this.settings.position.top = Number(eHeight) + Number(this.settings.offsetPadding)
			break;
			
			default :
				this.settings.position.top = 0 - Number(this.settings.dimensions.height) - Number(this.settings.offsetPadding)
			break;
		}		
	}	
});

var NavSprites = Behavior.create({
	initialize: function(parent, selectedPrefix, hoverSpeed, style) {
		element = this.element;
		this.parent = parent;
		this.selectedPrefix = selectedPrefix;
		this.hoverSpeed = hoverSpeed/1000;
		this.style = style;

		// throw the parent object's class into a variable
		var parentClass = element.readAttribute("class");

		// start a loop that cycles through each of the li elements inside the parent element
		element.childElements("li").each(function(subElement) {
			// create a few variables that we'll need during this function:
			// myClass = the class of the object we're currently inspecting
			// current = what the selected class should look like for the parent of the object we're currently inspecting
			var myClass = subElement.readAttribute("class");
			var current = parent + " " + selectedPrefix + myClass;

			// let's hide the CSS-defined background image, but only if this isn't the currently-selected item
			if (parentClass != current) {
				subElement.childElements("a").each(function(subSubElement){
					subSubElement.setStyle({backgroundImage:"none"});
				});
			}
		});
	},
	onmouseover: function(e) {
		var target = Event.element(e).up();
		var myClass = target.readAttribute("class");
		var parent = this.parent;
		var hoverSpeed = this.hoverSpeed;

		// create pseudo-link
		target.insert('<div class="' + this.parent + '-' + myClass + ' " style="display:none"></div>');
		// either slide or fade, depending on the style value
		if (this.style == "slide") {
			// slide down the pseudo-link
			$$("div."+ this.parent + "-" + myClass).each(function(item){
				Effect.BlindDown(item, {duration: hoverSpeed});
			});
		} else {
			// fade in the pseudo-link
			$$("div."+ this.parent + "-" + myClass).each(function(item){
				Effect.Appear(item, {duration: hoverSpeed});
			});
		}
	},
	onmouseout: function(e) {
		var target = Event.element(e).up();
		var myClass = target.readAttribute("class");
		var parent = this.parent;
		var hoverSpeed = this.hoverSpeed;

		// either slide or fade, depending on the style value
		if (this.style == "slide") {
			// slide up & destroy pseudo-link
			$$("div."+ this.parent + "-" + myClass).each(function(item){
				Effect.BlindUp(item, {duration: hoverSpeed, afterFinish: function callback(obj) {item.remove();}});
			});
		} else {
			// fade out & destroy pseudo-link
			$$("div."+ this.parent + "-" + myClass).each(function(item){
				Effect.Fade(item, {duration: hoverSpeed, afterFinish: function callback(obj) {item.remove();}});
			});
		}
	},
	onmousedown: function(e) {
		var target = Event.element(e).up();
		var myClass = target.readAttribute("class");
		var parent = this.parent;

		$$("div."+ this.parent + "-" + myClass).each(function(item){
			item.writeAttribute("class", parent + "-" + myClass + "-click");
		});
	},
	onmouseup: function(e) {
		var target = Event.element(e).up();
		var myClass = target.readAttribute("class");
		var parent = this.parent;

		$$("div."+ this.parent + "-" + myClass + "-click").each(function(item){
			item.writeAttribute("class", parent + "-" + myClass);
		});
	}
});

var InputLabel = Behavior.create({
	initialize: function(input_label) {
		this.input_label = input_label;
	},
	onfocus : function(e) {
		if(this.element.value == this.input_label){
			this.element.value = '';
		}
	},
	onblur : function(e) {
		if(this.element.value == ''){
			this.element.value = this.input_label;
		}
	}
})

// ImageFader
// Fades images in from a specified directory
// ToDos: 
//		1. set default values for duration and period so you don't have to pass them in
//		2. figure out how to pull a listing of files from a remote directory without 
//		   using ajax
//		3. include an option to use different scriptaculous effects


var ImageFader = Behavior.create({
	initialize: function(settings) {
		this.settings = $H();
		this.settings.random = true;
		this.settings.duration = settings.duration;
		this.settings.period = settings.period;
		this.settings.effect = settings.effect;
		this.settings.random = settings.random;
			
		// set some styles 
		var image = this.element.select('img');
		this.currentImage = image[0];
		this.__stylize();
		
		// get an array of images to rotate
		this.__getImages();

		//run the periodical executer -- there is a scope issue with using "this"
		var scope = this;
		new PeriodicalExecuter(function(){scope.__rotate()}, this.settings.period);
		//this.__fader();
	},
	
// beautifully rotate the images like a fox
	__rotate : function() {
		// load next random image, or the next image in the list
		if(this.settings.random) {
			this.images = this.__shuffle(this.images);
			if(this.currentImage.src.indexOf(this.images[0]) > 0) {
				src = this.images[1];
			} else {
				src = this.images[0]
			}
		} else {
			var i;
			//find the index of the current image
			this.images.each(function(item, index){
				if(this.currentImage.src.indexOf(item) > 0) {
					i = index;
				}
			}.bind(this));
			
			// set the source, either the next image or the first
			if((i + 1) < this.images.length) {
				src = this.images[i + 1];
			} else {
				src = this.images[0];
			}
		}
		
		// create the next image
		var image = new Element('img', {
							src: this.imagePath + '/' + src,
							alt: ''
					});
		this.nextImage = image;	
		
		switch (this.settings.effect) {
			case 'fade' : this.__fader(); break;
			case 'switch' : 
				this.settings.duration = 0;
				this.__fader(); break;
			case 'slide' : this.__slider(); break;
			default: this.__fader();
		}
		
		// save next image as current image
		this.currentImage = this.nextImage;		
	}, // __rotate
	
// fade a thing
	__fader : function() {
		// hide the image
		this.nextImage.setStyle({
			display: 'none',
			position: 'absolute'
		});			
		
		// stick it in the source
		this.element.appendChild(this.nextImage);

		// fade in next image and delete the old one
		var oldImage = this.currentImage;
		this.nextImage.appear({
			duration: this.settings.duration,
			afterFinish: function() {oldImage.remove()}
		});	
	}, // __fader

// slide a thing
	__slider : function() {
		// build the new image/div combo
		var div = new Element('div');
		var innerDiv = new Element('div');
		innerDiv.appendChild(this.nextImage);
		div.appendChild(innerDiv);
		
		// set a couple of style to position and remove some flickering
		div.setStyle({position: 'absolute', display: 'none'});
		
		// stick it in the source
		this.element.appendChild(div);

		var oldImage = this.currentImage.up().up();
		new Effect.SlideDown(div, {
			duration: this.settings.duration,
			afterFinish: function() {oldImage.remove()}
		});		
	}, // __slider

// set some necessary styles
	__stylize : function() {
		// get the width and height needed to keep the containers where they are
		var width = this.currentImage.getWidth();
		var height = this.currentImage.getHeight();
	
		// set the container's position
		this.element.setStyle({
			position: 'relative',
			width: width + 'px',
			height: height + 'px'
		});
		
		// set the images styles
		this.currentImage.setStyle({
			position: 'absolute'
		});
	}, // __stylize

// get an array of images for this particular fader
	__getImages : function() {
		// get the image path
		var input = this.element.select('input.lh_imagePath');
		var input = input[0];
		this.imagePath = input.value;
		
		// get the images (filenames)
		this.images = this.__pullImages(this.imagePath);
	}, // __getImages
	
// currently pulls the images from a second hidden field. somehow this needs to 
// happen automatically. however it works it needs to return an array of image 
// names 
	__pullImages : function() {
		var input = this.element.select('input.lh_images');
		var input = input[0];
		var input = input.value;
		input = input.split(',');
		
		return input;
	}, // __pullImages

// shuffle an array, surely there is a built in for this?	
	__shuffle : function(a) {
		var i = a.length;
		if ( i == 0 ) return false;
		while ( --i ) {
			var j = Math.floor( Math.random() * ( i + 1 ) );
			var tempi = a[i];
			var tempj = a[j];
			a[i] = tempj;
			a[j] = tempi;
		}
	return a;  
	} // __shuffle
});

var Pop = Behavior.create({
	initialize: function() {
		// get the post_id from a hidden field
		var id = this.element.getElementsByClassName('post_id');
		this.post_id = $F(id[0]);
	},
	onclick : function(e) {
		var source = Event.element(e);

		if($(source).hasClassName('pop')) return this.__pop(e);
		if($(source).hasClassName('unpop')) return this.__unpop(e);
		if($(source).hasClassName('popped')) return false;
		if($(source).hasClassName('unpopped')) return false;
		if($(source).hasClassName('share')) return this.__share(e);
		//return false;
	},
	__pop : function(e) {
		var source = Event.element(e);
		Event.stop(e);
		
		// ajax parameters 
		var url = $F('base_url') + '/posts/pop';
		var post = $H({
			post_id: this.post_id
		}).toQueryString();

		//ajax request
		new Ajax.Request(url, {
			method: 'post',
			postBody: post
		});
		source.removeClassName('pop');
		source.addClassName('popped');
		source.update('thanks');
		return false;
	},
	__unpop : function(e) {
		var source = Event.element(e);
		Event.stop(e);
		
		// ajax parameters 
		var url = $F('base_url') + '/posts/pop';
		var post = $H({
			post_id: this.post_id,
			delta: -1
		}).toQueryString();

		//ajax request
		new Ajax.Request(url, {
			method: 'post',
			postBody: post
		});		
		source.removeClassName('unpop');
		source.addClassName('unpopped');
		source.update('sorry');
		return false;
	},
	__share : function(e) {
		$('email_friend').toggle();
		return false;
	}
});

var Modal = Behavior.create({
	initialize: function(content) {
		this.content = content || 'Please wait ...';
		this.overlay = new Element('div', { id: 'modal_overlay', 'class': 'overlay', style: 'position: fixed; top: 0pt; left: 0pt; width: 100%; height: 100%; z-index: 9998;'});
		this.container = new Element('div', { id: 'modal_container', style: 'z-index: 9999;'});
		Element.insert(this.element, this.overlay);
		Element.insert(this.element, this.container);
	},
	onclick: function(e){
		var target = Event.element(e)
		if($(target).hasClassName('overlay')){
			this.close();
			return false;
		}
		if($(target).hasClassName('close')){
			this.close();
			return false;
		}
		if($(target).hasClassName('cancel')){
			this.close();
			return false;
		}
	},
	onKeydown: function(e){
		if(e.keyCode == Event.KEY_ESC){
			this.close();
			return false;		
		}
	},
	open: function(content, ajaxUrl, ajaxOptions){
		if(ajaxUrl){
			new Ajax.Request(ajaxUrl,{
				method: ajaxOptions.method,
				postBody: ajaxOptions.variables,
				asynchronous: false,
				onComplete: function(request){
					content = request.responseText;
				}
			});		
		}else{
			content = content || this.content;
		}
		this.element.show();
		this.container.update(content);
		this.center(this.container);
	},
	close: function(e){
		this.element.hide();
	},
	center: function(element){
		if(!element._absolutized){
			element.setStyle({
				position: 'absolute'
			}); 
			element._absolutized = true;
		}
		var dimensions = element.getDimensions();
		Position.prepare();
		var offset_left = (Position.deltaX + Math.floor((this.getWindowWidth() - dimensions.width) / 2));
		var offset_top = (Position.deltaY + ((this.getWindowHeight() > dimensions.height) ? Math.floor((this.getWindowHeight() - dimensions.height) / 2) : 0));
		element.setStyle({
			top: ((dimensions.height <= this.getDocumentHeight()) ? ((offset_top != null && offset_top > 0) ? offset_top : '0') + 'px' : 0),
			left: ((dimensions.width <= this.getDocumentWidth()) ? ((offset_left != null && offset_left > 0) ? offset_left : '0') + 'px' : 0)
		});
	},
	getWindowWidth: function(){
		return (self.innerWidth || document.documentElement.clientWidth || document.body.clientWidth || 0);
	},
	getWindowHeight: function(){
		return (self.innerHeight ||  document.documentElement.clientHeight || document.body.clientHeight || 0);
	},
	getDocumentWidth: function(){
		return Math.min(document.body.scrollWidth,this.getWindowWidth());
	},
	getDocumentHeight: function(){
		return Math.max(document.body.scrollHeight,this.getWindowHeight());
	}
});

FancyMenu = Behavior.create({
	initialize: function(settings) {
		//default settings
		this.settings = $H({
			orientation: settings.orientation || 'horizontal', 
			effect: settings.effect || 'blind',
			effectSettings: settings.effectSettings || $H({duration: 3}),
			submenuWidth: settings.submenuWidth || 100
		});

		// load all the submenus into this object
		this.submenu = false;
 		var submenuElements = this.element.select('.lh_submenu');
		this.submenus = $A();
		var scope = this;
		submenuElements.each(function (e) {
			scope.submenus.push(new FancyMenu.Submenu(e, scope));
		});
	},

// internal methods ------------------------------------------------------------		
	__hideSubmenus: function() {
		this.submenus.each(function(item){
			if(item.visible) item.__hide();
		});
	}
});

FancyMenu.Submenu = Behavior.create({
	initialize: function(fancyMenu) {
		this.fancyMenu = fancyMenu;
		this.container = this.element.select('div.lh_container');		
		this.container = this.container[0];
		this.visible = false;
		// get a quasi-unique id for effect scopes
		var aDate = new Date(); 
		this.id = aDate.getTime() + Math.random();
		
		// set its styles
		this.container.setStyle({
			position: 'absolute',
			width: this.fancyMenu.settings.get('submenuWidth') + 'px'
		});
	},
	
// events ----------------------------------------------------------------------
	onmouseover: function(e) {
		Event.stop(e);
		source = Event.element(e);
		if(!this.__within(this.container, e.pointerX(), e.pointerY())) {
			this.__show();
		}		
	},
	onmouseout: function(e) {
		Event.stop(e);
		if(!this.__within(this.container, e.pointerX(), e.pointerY())) {
			this.__hide();
		}
	},
	
// internal methods ------------------------------------------------------------			
	__show : function() {				
		if(this.container && !this.visible) {
			this.fancyMenu.__hideSubmenus();
			var id = this.id;			
			var scope = this;
			this.container.blindDown({
				duration: '.2',
				queue: {scope: 'menu' + id, position: 'end'},
				afterFinish: function() {scope.visible = true}				
			});		
		} else {
			return false;
		}
	},
	__hide : function() {
		if(this.container && this.visible) {		
			var id = this.id;
			var scope = this;				
			this.container.blindUp({
				duration: '.1',
				queue: {scope: 'menu' + id, position: 'end'},
				afterFinish: function() {scope.visible = false}				
			});				
		} else {
			return false;
		}
	},
	__within : function(element, x, y) {
	    this.xcomp = x;
	    this.ycomp = y;
	    this.offset = Element.cumulativeOffset(element);

	    return (y >= this.offset[1] &&
	            y <  this.offset[1] + element.offsetHeight &&
	            x >= this.offset[0] &&
	            x <  this.offset[0] + element.offsetWidth);
	}
});


var CharacterCountdown = Behavior.create({
	initialize: function(maxLength, entryField, countField) {
		this.maxLength = maxLength;
		this.entryField = $(entryField);
		this.entryField.value = '';
		this.countField = $(countField);
		this.countField.update(this.maxLength);
	},
	onkeyup: function(e) {
		var target = Event.element(e);
		if(Element.identify(target) == Element.identify(this.entryField)){
			stringLength = this.entryField.value.length;
			charsLeft = this.maxLength - stringLength;
			this.countField.update(charsLeft);
			if(charsLeft < 10){
				this.countField.removeClassName('yield');
				this.countField.addClassName('stop');
			} else if(charsLeft < 20){
				this.countField.removeClassName('stop');
				this.countField.addClassName('yield');			
			} else {
				this.countField.removeClassName('yield');
				this.countField.removeClassName('stop');
			}
			
		}
	},
	onsubmit: function(e) {
		stringLength = $(this.entryField).value.length;
		if(stringLength > this.maxLength){
			alert('Sorry, you are limited to '+ this.maxLength + ' characters.');
			return false;
		}
	}
});
